/*******************************************************************************
 * Copyright 2012 momock.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.sun.dailyprj.util;

import android.content.Context;
import android.os.Environment;
import android.os.Process;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import static android.os.Environment.getExternalStorageState;

public class Logger{
    public static final int LEVEL_ALL = 0;

    public static final int LEVEL_DEBUG = 3;

    public static final int LEVEL_INFO = 4;

    public static final int LEVEL_WARN = 5;

    public static final int LEVEL_ERROR = 6;

    public static final int LEVEL_NONE = 7;

    static PrintStream logStream = null;
    static String logName = "OTA.DA";
    static String logFileName = "log.txt";
    static String logDirName = "carota";
    static int logLevel = LEVEL_DEBUG;
    static boolean enabled = true;
    static String appName;
    private static File logDir;

    private static void writeLogFile(String msg) {
        if(null != logStream) {
            logStream.println(msg);
            logStream.flush();
//        } else {
//            System.out.println(msg);
//            System.out.flush();
        }
    }

    public static void open(Context context, final String name, int maxLogFiles, int level) {
        if(maxLogFiles < 1)
            throw new IllegalArgumentException("maxLogFiles must be greater than 0");
        if(level < 0 || level > 7) throw new IllegalArgumentException("level error");
        if(!enabled) return;
        appName = context.getPackageName();
        logName = name;
        logFileName = logName + "[" + new SimpleDateFormat("yyyyMMddHHmmss", Locale.US).format(new Date())
                + "]" + Process.myPid() + ".log";
        if(null == logStream) {
            try{
                if(getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                    logDir = new File(context.getExternalCacheDir(), logDirName);
                    logDir.mkdirs();
                } else {
                    logDir = context.getCacheDir();
                }
                if(logDir != null) {
                    Log.d("Logger", logDir.getAbsolutePath());
                    String[] fs = logDir.list(new FilenameFilter(){

                        @Override
                        public boolean accept(File dir, String filename) {
                            return filename.startsWith(logName + "[") && filename.endsWith(".log");
                        }

                    });
                    List<String> allLogs = new ArrayList<String>();
                    for(int i = 0; fs != null && i < fs.length; i++)
                        allLogs.add(fs[i]);
                    Collections.sort(allLogs);
                    for(int i = 0; i < allLogs.size() - maxLogFiles + 1; i++)
                        new File(logDir, allLogs.get(i)).delete();
                    logStream = new PrintStream(new FileOutputStream(new File(logDir, logFileName), false));
                }
            } catch (IOException e) {
                Log.e("Logger", "Fails to create log file!", e);
            }
        }//android.util.Log.isLoggable()
        logLevel = level;
        writeLogFile("========== Logger Begin ==========");
    }

    public static File[] getLogList() {
        if(logDir == null) return null;
        return logDir.listFiles(new FilenameFilter(){
            @Override
            public boolean accept(File dir, String filename) {
                return filename.startsWith(logName + "[") && filename.endsWith(".log");
            }
        });
    }

    public static String getLogDirPath() {
        if(logDir == null) {
            return null;
        } else {
            return logDir.getAbsolutePath();
        }
    }


//    public static void open(Context context, String logfilename, int level) {
//        if (!enabled) return;
//        logFileName = logfilename;
//        if (logStream == null) {
//            logStream = System.out;
//            File logDir = null;
//            try {
//                if (getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//                    logDir = Environment.getExternalStorageDirectory();
//                } else if (context != null) {
//                    logDir = context.getCacheDir();
//                }
//                if (logDir != null) {
//                    android.util.Log.d("Logger", logDir.getAbsolutePath());
//                    logStream = new PrintStream(new FileOutputStream(new File(logDir, logFileName), false));
//                }
//            } catch (IOException e) {
//                android.util.Log.e("Logger", "Fails to create log file!", e);
//            }
//        }
//        logLevel = level;
//        writeLogFile("========== Logger Begin ==========");
//    }

    public static void close() {
        if(!enabled) return;
        if(logStream == null) return;
        logStream.println("========== Logger End   ==========");
        logStream.close();
        logStream = null;
        logDir = null;
    }

    static String getLog(String level, String msg, Throwable t) {
        StackTraceElement trace = t.getStackTrace()[2];
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);
        return "[" + level + "] " + sdf.format(new Date()) + " in " + trace.getFileName() + "(" + trace.getLineNumber() + ") >" + msg;
    }

    static String getSourceInfo(Throwable t) {
        StackTraceElement trace = t.getStackTrace()[2];
        return trace.getFileName() + "(" + trace.getLineNumber() + ")";
    }

    static String logout(String tag, int level, String msg, Object... args) {
        if(!enabled || logLevel > level || null == msg) {
            return null;
        }
        String realMsg = args.length > 0 ? String.format(msg, args) : msg;
        Throwable t = new Throwable();
        writeLogFile(getLog(tag, realMsg, t));
        return realMsg + " @ " + getSourceInfo(t);
    }

    public static void debug(String msg, Object... args) {
        String logMsg = logout("DEBUG", LEVEL_DEBUG, msg, args);
        if(null != logMsg) {
            Log.d(logName, logMsg);
        }
    }

    public static void info(String msg, Object... args) {
        String logMsg = logout("INFO", LEVEL_INFO, msg, args);
        if(null != logMsg) {
            Log.i(logName, logMsg);
        }
    }

    public static void warn(String msg, Object... args) {
        String logMsg = logout("WARN", LEVEL_WARN, msg, args);
        if(null != logMsg) {
            Log.w(logName, logMsg);
        }
    }

    public static void error(String msg, Object... args) {
        String logMsg = logout("ERROR", LEVEL_ERROR, msg, args);
        if(null != logMsg) {
            Log.e(logName, logMsg);
        }
    }

    public static String getStackTrace(Throwable e) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(baos);
        e.printStackTrace(ps);
        return new String(baos.toByteArray());
    }

    public static void error(Throwable e) {
        if(!enabled || logLevel > LEVEL_ERROR) {
            return;
        }
        error(getStackTrace(e));
    }

    public static void check(boolean condition, String msg, Object... args) {
        if(!condition) {
            String logMsg = logout("ASSERT", LEVEL_ERROR, null == msg ? "" : msg, args);
            if(null != logMsg) {
                Log.e(logName, logMsg);
                throw new RuntimeException(logMsg);
            }
        }
    }

    public static boolean isEnabled() {
        return enabled;
    }

    public static void setEnabled(boolean enabled) {
        Logger.enabled = enabled;
    }
}
